/**
 * 
 */
package uk.ac.ebi.curtain.utils.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.output.CountingOutputStream;

/**
 * @author mhaimel
 *
 */
public abstract class AbstractIO {

	public BufferedReader getBufferedReader() throws IOException{
		return new BufferedReader(getReader());
	}
	
	public BufferedReader getBufferedReader(int buffSize) throws IOException{
		return new BufferedReader(getReader(),buffSize);
	}
	
	public Reader getReader() throws IOException{
		return new InputStreamReader(getInputStream());
	}

	public InputStream getInputStream() throws IOException {
		return wrappedInputStream(getBaseInputStream(), getStreamCompression());
	}
	
	public BufferedInputStream getBufferedInputStream() throws IOException {
		return new BufferedInputStream(getInputStream());
	}
	
	public DataInputStream getDataInputStream() throws IOException {
		return new DataInputStream(getBufferedInputStream());
	}
	
	protected abstract CompressionFormat[] getStreamCompression();

	protected abstract InputStream getBaseInputStream() throws IOException;
	
	private InputStream wrappedInputStream(InputStream stream, CompressionFormat ... compArr) throws IOException{
		InputStream in = stream;
		for(CompressionFormat comp : compArr){
			in = comp.wrapStream(in);
		}
		return in;
	}
	
	public PrintWriter getPrintWriter() throws IOException{
		return new PrintWriter(getBufferedWriter());
	}
	
	public PrintStream getPrintStream() throws IOException{
		return new PrintStream(getBufferedOutputStream());
	}
	
	public BufferedWriter getBufferedWriter() throws IOException{
		return new BufferedWriter(getWriter());
	}
	
	public Writer getWriter() throws IOException{
		return new OutputStreamWriter(getOutputStream());
	}
		
	public OutputStream getOutputStream() throws IOException{
		return wrappedOutputStream(getBaseOutputStream(),getStreamCompression());
	}
	
	public BufferedOutputStream getBufferedOutputStream() throws IOException{
		return new BufferedOutputStream(getOutputStream());
	}
	
	public CountingOutputStream getCountingOutputStream() throws IOException{
		return new CountingOutputStream(getBufferedOutputStream());
	}
	
	public DataOutputStream getDataOutputStream() throws IOException{
		return new DataOutputStream(getBufferedOutputStream());
	}
	
	protected abstract OutputStream getBaseOutputStream() throws IOException;
	
	private OutputStream wrappedOutputStream(OutputStream stream,  CompressionFormat ... compArr) throws IOException{
		OutputStream out = stream;
		for(CompressionFormat comp : compArr){
			out = comp.wrapStream(out);
		}
		return out;
	}
	
	public static enum CompressionFormat{
		Gzip("gz","gzip"),
		Zip("zip");
		
		private final Set<String> app = new ConcurrentSkipListSet<String>();
		private final String defSuffix;
		
		private CompressionFormat(String ... appends) {
			defSuffix = appends[0];
			app.addAll(Arrays.asList(appends));
		}
		
		public static CompressionFormat getCompression(File file){
			CompressionFormat comp = null;
			String name = file.getName();
			int idx = name.lastIndexOf('.');
			if(idx > 0 && idx < name.length()-1){
				String pdx = name.substring(idx+1);
				for(CompressionFormat curr : values()){
					if(curr.app.contains(pdx)){
						comp = curr;
						break;
					}
				}
			}
			return comp;
		}
		
		public String getDefaultSuffix() {
			return defSuffix;
		}
		
		public InputStream wrapStream(InputStream input) throws IOException{
			InputStream stream = input;
			switch (this) {
			case Gzip:
				stream = new GZIPAppendInputStream(stream);
				break;
			case Zip:
				stream = new ZipInputStream(stream);
				break;
			}
			return stream;
		}
		
		public OutputStream wrapStream(OutputStream out) throws IOException{
			OutputStream stream = out;
			switch (this) {
			case Gzip:
				stream = new GZIPOutputStream(stream);
				break;
			case Zip:
				stream = new ZipOutputStream(stream);
				break;
			}
			return stream;
		}
		
	}
	
}
